﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Powerpoint
{
    public class Client
    {
        // The port number for the remote device.
        //private const int port = 2000;
        private bool connected = false;

        // ManualResetEvent instances signal completion.
        private ManualResetEvent connectDone =
            new ManualResetEvent(false);
        private ManualResetEvent sendDone =
            new ManualResetEvent(false);
        private ManualResetEvent receiveDone =
            new ManualResetEvent(false);

        // The response from the remote device.
        private static String response = String.Empty;

        // 
        public IPAddress ipAddress;
        public int serverPort;

        //
        private static MainWindow gui;

        // Delegate
        private delegate void OneArgDelegate(String arg);
        private delegate void noArgDelegate();
        private delegate void SlideArgDelegate(int page);
        private delegate void ImgArgDelegate(byte[] buffer);

        private Socket client;
        public ConnectionHandler chandler { get; set; }

        private static int nb_datapkg_sent = 0;
        private static int nb_datapkg_rcv = 0;

        //
        private static NetworkStream myNetStream;

        public void setupConnection(String ipStr, int portInt, MainWindow gui_hdl)
        {
            ipAddress = IPAddress.Parse(ipStr);
            serverPort = portInt;
            gui = gui_hdl;
        }

        public void closeConnection()
        {
            if (connected)
            {
                client.Shutdown(SocketShutdown.Both);
                chandler.closeConnection();
                chandler.setReceiveDone();
                chandler.setSendDone();

                connected = false;
            }

        }


        public bool getConnectionStatus()
        {
            return connected;
        }

        public void StartClient()
        {
            // Connect to a remote device.
            try
            {
                // Establish the remote endpoint for the socket.
                // The name of the 
                // remote device is "host.contoso.com".
                //IPHostEntry ipHostInfo = Dns.Resolve("localhost");
                //IPAddress ipAddress = ipHostInfo.AddressList[0];

                //IPAddress ipAddress = IPAddress.Loopback;
                IPEndPoint remoteEP = new IPEndPoint(ipAddress, serverPort);

                // Create a TCP/IP socket.
                client = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);

                // Connect to the remote endpoint.
                client.BeginConnect(remoteEP,
                    new AsyncCallback(ConnectCallback), client);
                gui.textBoxEvents.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                    new OneArgDelegate(gui.updateEventBox),
                    "Verbindungsaufbau...");

                connectDone.WaitOne();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // Create the state object.
                StateObject state = new StateObject();

                // Retrieve the socket from the state object.
                Socket client = (Socket)ar.AsyncState;
                state.workSocket = client;

                // Complete the connection.
                client.EndConnect(ar);

                Console.WriteLine("Socket connected to {0}",
                    client.RemoteEndPoint.ToString());

                // Create new connection handler.
                chandler = new ConnectionHandler(state);
                state.chandler = chandler;

                Thread receiveing = new Thread(chandler.receiveThread);
                receiveing.Start();

                Thread sending = new Thread(chandler.sendThread);
                sending.Start();

                // Signal that the connection has been made.
                connectDone.Set();
                connected = true;

                newEvent(String.Format("Erfolgreich verbunden mit: {0}:{1}", getIPString(client), getPort(client)));

                myNetStream = new NetworkStream(state.workSocket);

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public static NetworkStream getNetStream()
        {
            return myNetStream;
        }

        public static void Receive(Socket client, StateObject state)
        {
            try
            {
                // Begin receiving the data from the remote device.
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void ReceiveCallback(IAsyncResult ar)
        {
            String content = String.Empty;

            try
            {
                // Retrieve the state object and the client socket 
                // from the asynchronous state object.
                StateObject state = (StateObject)ar.AsyncState;
                Socket client = state.workSocket;

                // Read data from the remote device.
                int bytesRead = client.EndReceive(ar);
                if (bytesRead > 0)
                {
                    //newEvent(String.Format("{0} bytes empfangen...", bytesRead));

                    // There might be more data, so store the data received so far.
                    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

                    // Check for end-of-file tag. If it is not there, read 
                    // more data.
                    content = state.sb.ToString();

                    //if (content.IndexOf('¿') > 0)
                    if(content.Contains("@EOF"))
                    {
                        // All the data has been read from the 
                        // client. Display it on the console.
                        state.chandler.setReceiveDone();
                        //Console.Write("---------- {0}:{1}: ", IPAddress.Parse(((IPEndPoint)client.RemoteEndPoint).Address.ToString()), ((IPEndPoint)client.RemoteEndPoint).Port.ToString());
                        //Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
                        //content.Length, content);
                        //newEvent(String.Format("Vollstaendiges Paket empfangen: {0} bytes.\n Daten: {1}", content.Length, "content"));

                        // SLIDES
                        int set_to_page = 1;
                        string[] msg_parts;
                        msg_parts = content.Split('$');
                                              

                        if (msg_parts[1].Equals("nextSlide"))
                        {
                            set_to_page = int.Parse(msg_parts[2]);
                            gui.textBoxEvents.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                                new SlideArgDelegate(gui.nextSlideEvent),
                                set_to_page);
                        }
                        else if (msg_parts[1].Equals("previousSlide"))
                        {
                            set_to_page = int.Parse(msg_parts[2]);
                            gui.textBoxEvents.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                                new SlideArgDelegate(gui.previousSlideEvent),
                                set_to_page);
                        }
                        // SLIDES OFF
                        else if (msg_parts[1].Equals("notificationTrue"))
                        {
                            gui.rbNotificationUser1.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new noArgDelegate(gui.setNotificationUser1True));


                        }
                        else if (msg_parts[1].Equals("notificationFalse"))
                        {
                            gui.rbNotificationUser1.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new noArgDelegate(gui.setNotificationUser1False));
                        }
                        else
                        {
                                gui.textBoxEvents.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                                new ImgArgDelegate(gui.updateImg1),
                                state.buffer);
                        }

                        nb_datapkg_rcv++;
                        newDataEvent();
                        // Clear data for next message.
                        state.sb.Clear();
                    }
                    else
                    {
                        // Get the rest of the data.
                        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                            new AsyncCallback(ReceiveCallback), state);
                        newEvent("Empfange restliche Bytes...");
                    }

                }
                else
                {
                    // All the data has arrived; put it in response.
                    if (state.sb.Length > 1)
                    {
                        response = state.sb.ToString();
                       // Console.WriteLine(" Data: {0}", response);
                        state.sb.Clear();
                    }
                    // Signal that all bytes have been received.
                    //receiveDone.Set();
                    state.chandler.setReceiveDone();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        //public static void Send(Socket client, byte[] byteData, StateObject state)
        //{
        //    // Convert the string data to byte data using ASCII encoding.
        //    //byte[] byteData = Encoding.ASCII.GetBytes(data);

        //    // Begin sending the data to the remote device.
        //    client.BeginSend(byteData, 0, byteData.Length, 0,
        //        new AsyncCallback(SendCallback), state);
        //}

        public static void Send(Socket client, byte[] byteData, StateObject state)
        {
            // Convert the string data to byte data using ASCII encoding.
            //byte[] byteData = Encoding.ASCII.GetBytes(data);

            // Begin sending the data to the remote device.
            //client.BeginSend(byteData, 0, byteData.Length, 0,
            //    new AsyncCallback(SendCallback), state);
            NetworkStream tmp = getNetStream();
            tmp.BeginWrite(byteData, 0, byteData.Length, new AsyncCallback(SendCallback), state);
        }

        // veraltet
        public static void SendString(Socket client, String data, StateObject state)
        {
            // Convert the string data to byte data using ASCII encoding.
            byte[] byteData = Encoding.ASCII.GetBytes(data);

            Send(client, byteData, state);
        }

        private static void SendCallback(IAsyncResult ar)
        {
            StateObject state = (StateObject)ar.AsyncState;
            // Retrieve the socket from the state object.
            Socket client = state.workSocket;

            try
            {
                // Retrieve the socket from the state object.
                //Socket client = (Socket)ar.AsyncState;

                // Complete sending the data to the remote device.
                //int bytesSent = client.EndSend(ar);
                //int bytesSent = -1;
                myNetStream.EndWrite(ar);
                //Console.WriteLine("----------\nSent {0} bytes to server.", bytesSent);
                //newEvent(String.Format("Sent {0} bytes to server.", bytesSent));
                nb_datapkg_sent++;
                newDataEvent();
                // Signal that all bytes have been sent.
                //sendDone.Set();
                // Signal Thread.
                state.chandler.setSendDone();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        public static void newEvent(String strEve)
        {
            gui.textBoxEvents.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                new OneArgDelegate(gui.updateEventBox),
                strEve);
        }

        private static void newDataEvent()
        {
            String strData;

            strData = "sent: " + nb_datapkg_sent + " / rcv: " + nb_datapkg_rcv;
            
            gui.nbDataPkg.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                new OneArgDelegate(gui.updateDataBox),
                strData);
        }
        private String getIPString(Socket socket)
        {
            String ip = ((IPEndPoint)socket.RemoteEndPoint).Address.ToString();
            return ip;
        }

        private int getPort(Socket socket)
        {
            return ((IPEndPoint)socket.RemoteEndPoint).Port;
        }
    }
}
